#include <unistd.h>
#include <sstream>
#include <boost/date_time/gregorian/gregorian.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/date_time/posix_time/posix_time_io.hpp>
#include "SystemStatus.h"
#include "../db/TableEnums.h"
#include "Queues.h"
#include "Observers.h"
#include "../utils/Time.h"

using namespace boost::posix_time;
using namespace boost::gregorian;
using namespace kc_sf;

// ------------------------------------------------------- //

SystemStatus::SystemStatus( int period )
: s_period( period ),
  s_connections_in( 0 ),
  s_connections_out( 0 ),
  s_input_msg_queue_size( 0 ),
  s_output_msg_queue_size( 0 )
{
	std::string table_name = StatusTableTypeToString.at( StatusSystemStart );

	DatabaseRecord column_names;
	column_names.push_back( "started" );

	ptime now = second_clock::local_time();
	sleep( s_period - now.time_of_day().seconds() % s_period );

	DatabaseRecord values;
	values.push_back( TimeToString()( now ) );

	s_db_start_id = boost::lexical_cast< std::string >( s_conn.insertInto( table_name, column_names, values ) );
}

// ------------------------------------------------------- //

SystemStatus::~SystemStatus()
{
	std::string table_name = StatusTableTypeToString.at( StatusSystemStart );

	DatabaseRecord column_names;
	column_names.push_back( "stopped" );

	DatabaseRecord values;

	ptime now = second_clock::local_time();
	values.push_back( TimeToString()( now ) );

	std::stringstream last_id_query;
	last_id_query << "SELECT id FROM " << table_name
			<< "ORDER BY started DESC LIMIT 1";

	DatabaseTable last_id_result = s_conn.query( last_id_query.str() );
	DatabaseRecord last_id_record = last_id_result.getRecord(0);
	std::string last_id = last_id_record[0];

	std::cout << "last id: " << last_id << std::endl;

	std::stringstream insert_stopped_query;
	insert_stopped_query << "UPDATE " << table_name
			<< "SET stopped=" << TimeToString()( now )
			<< "WHERE id=" << last_id;

	std::string query = insert_stopped_query.str();
	std::cout << "query: " << query << std::endl;

	s_conn.query( query );
}

// ------------------------------------------------------- //

void SystemStatus::start()
{
	while( true )
	{
		ptime now = makeSnapshot();
		sleep( s_period - now.time_of_day().seconds() % s_period );
	}
}

// ------------------------------------------------------- //

ptime SystemStatus::makeSnapshot()
{
	std::string table_name = StatusTableTypeToString.at( StatusSnapshot );

	DatabaseRecord column_names;
	column_names.push_back( "system_start_id" );
	column_names.push_back( "date" );
	column_names.push_back( "input_queue_size" );
	column_names.push_back( "output_queue_size" );
	column_names.push_back( "msgs_fwd" );
	column_names.push_back( "msgs_sent" );
	column_names.push_back( "msgs_in_quarantine" );
	column_names.push_back( "msgs_deleted" );
	column_names.push_back( "incoming_connections" );
	column_names.push_back( "outgoing_connections" );

	DatabaseRecord values;

	unsigned int input_msgs_queue_delta = InputMessageQueueObserver.size() - s_input_msg_queue_size;
	unsigned int output_msgs_queue_delta = OutputMessageQueueObserver.size() - s_output_msg_queue_size;
	s_input_msg_queue_size = InputMessageQueueObserver.size();
	s_output_msg_queue_size = OutputMessageQueueObserver.size();

	unsigned int connections_in_delta = AllConnectionsInObserver.size() - s_connections_in;
	unsigned int connections_out_delta = AllConnectionsOutObserver.size() - s_connections_out;
	s_connections_in = AllConnectionsInObserver.size();
	s_connections_out = AllConnectionsOutObserver.size();

	ptime now = second_clock::local_time();

	values.push_back( s_db_start_id );
	values.push_back( TimeToString()( now ) );

	values.push_back( boost::lexical_cast< std::string >( input_msgs_queue_delta ) );
	values.push_back( boost::lexical_cast< std::string >( output_msgs_queue_delta ) );
	values.push_back( boost::lexical_cast< std::string >( RedirectObserver.size() ) );
	values.push_back( boost::lexical_cast< std::string >( SentObserver.size() ) );
	values.push_back( boost::lexical_cast< std::string >( QuarantineObserver.size() ) );
	values.push_back( boost::lexical_cast< std::string >( DeletedObserver.size() ) );
	values.push_back( boost::lexical_cast< std::string >( connections_in_delta ) );
	values.push_back( boost::lexical_cast< std::string >( connections_out_delta ) );

	s_conn.insertInto( table_name, column_names, values );

	return now;
}
